using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Reflection;
using System.Linq;

namespace Framework
{
	public static class EnumUtil
	{
        #region Parse<T>
        /// <summary>
        /// Parse a string and return an enumerated type. Throws exception if string is invalid
        /// </summary>
        /// <param name="value">The value.</param>
        /// <returns></returns>
        public static T Parse<T>(string value)
        {
            return (T)Enum.Parse(typeof(T), value, true);
        }

	    /// <summary>
	    /// Parse a string and return an enumerated type. Throws exception if string is invalid
	    /// </summary>
	    /// <param name="value">The value.</param>
	    /// <param name="defaultValue"> </param>
	    /// <returns></returns>
	    public static T Parse<T>(string value, T defaultValue)
        {
            try
            {
                return (T)Enum.Parse(typeof(T), value, true);
            }
            catch (Exception)
            {
                return defaultValue;
            }
        }
        #endregion

        #region Parse (string)
		/// <summary>
		/// Parse a string and return an enumerated type. Throws exception if string is invalid
		/// </summary>
		/// <example>
		/// DatabaseNameType databaseName = (DatabaseNameType)EnumUtil.Parse(typeof(DatabaseNameType), "Mt");
		/// </example>
		/// <param name="enumType">Type of the enumeration</param>
		/// <param name="enumName">String that represents that type</param>
		/// <returns></returns>
		public static object Parse(Type enumType, string enumName)
		{
			return Enum.Parse(enumType, enumName, true);
		}

		/// <summary>
		/// Parse a string and return an enumerated type, uses defaultValue if string is invalid
		/// </summary>
		/// <example>
		/// DatabaseNameType databaseName = (DatabaseNameType)EnumUtil.Parse(typeof(DatabaseNameType), "Mt", DatabaseNameType.Mt);
		/// </example>
		/// <param name="enumType">Type of the enumeration</param>
		/// <param name="enumName">String that represents that type</param>
		/// <param name="defaultValue">Default value if string is invalid</param>
		/// <returns></returns>
		public static object Parse(Type enumType, string enumName, Enum defaultValue)
		{
			try
			{
                if (Is.NotEmptyString(enumName))
                {
                    //return Enum.Parse(enumType, enumName, true);

                    //since we cant do a generic type constraint
                    if (!enumType.IsEnum)
                    {
                        throw new ArgumentException("Must pass an Enum Type");
                    }
                    if (Enum.GetNames(enumType).Any(e => Is.EqualString(e, enumName)))
                    {
                        return Enum.Parse(enumType, enumName, true);
                    }
                }
			}
			catch { }

			return defaultValue;
		}
		#endregion

		#region Parse (int)
		public static object Parse(Type enumType, int enumValue, Enum defaultValue)
		{
			if (Enum.IsDefined(enumType, enumValue))
			{
				return Enum.ToObject(enumType, enumValue);
			}

			return defaultValue;
		}
		#endregion

		#region ParseObject
		/// <summary>
		/// Usefull for Passing a ViewState enum
		/// </summary>
		/// <param name="enumType"></param>
		/// <param name="enumValue"></param>
		/// <param name="defaultValue"></param>
		/// <returns></returns>
		public static object ParseObject(Type enumType, object enumValue, Enum defaultValue)
		{
			if (enumValue == null)
			{
				return defaultValue;
			}
			if (enumValue is Enum)
			{
				return Parse(enumType, (int)enumValue, defaultValue);
			}
			return Parse(enumType, (string)enumValue, defaultValue);
		}
		#endregion

        #region GetName
        /// <summary>
        /// Gets the name of the enumeration
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="enumType">Type of the enum.</param>
        /// <returns>Description</returns>
        public static string GetName<T>(T enumType)
        {
            return Enum.GetName(typeof(T), enumType);
        }
        #endregion

		#region GetDescription
		/// <summary>
		/// Convert int to Enum and then return the value of the Description attribute for an Enum
		/// </summary>
		/// <param name="enumType"></param>
		/// <param name="enumValue"></param>
		/// <param name="defaultValue"></param>
		/// <returns>Description</returns>
		public static string GetDescription(Type enumType, int enumValue, Enum defaultValue)
		{
			Enum value = (Enum)EnumUtil.Parse(enumType, enumValue, defaultValue);

			return GetDescription(value);
		}

		/// <summary>
		/// Return the value of the Description attribute for an Enum
		/// </summary>
		/// <param name="value">Enum to get Description for</param>
		/// <returns>Description</returns>
		public static string GetDescription(Enum value)
		{
			FieldInfo fi = value.GetType().GetField(value.ToString());
			
			DescriptionAttribute[] attributes = (DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false);
			
			return (attributes.Length > 0) ? attributes[0].Description : value.ToString();
		}
		#endregion

        #region GetValues
        /// <summary>
        /// Get a list of enumerated values.
        /// </summary>
        /// <returns></returns>
        public static IList<T> GetValues<T>()
        {
            return (from object value in Enum.GetValues(typeof(T)) select (T)value).ToList();
        }
        #endregion

        #region Length
        public static int Length<T>()
        {
            if (!typeof(T).IsEnum)
            {
                throw new ArgumentException("Type T must be an enumeration", "T");
            }

            return Enum.GetNames(typeof(T)).Length;
        }
        #endregion

        #region IsSet
        /// <summary>
        /// Determines whether the specified enumeration flag value is set.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="value">The value.</param>
        /// <param name="flags">The flags.</param>
        /// <returns>
        /// 	<c>true</c> if the specified value is set; otherwise, <c>false</c>.
        /// </returns>
        public static bool IsSet<T>(T value, T flags) where T : struct
        {
            // You can add enum type checking to be perfectly sure that T is enum, this have some cost however
            if (!typeof(T).IsEnum)
            {
                throw new ArgumentException("T is not an enumeration");
            }

            var longFlags = Convert.ToInt64(flags);
            var longValue = Convert.ToInt64(value);

            return (longValue & longFlags) == longFlags;
        }
        #endregion
    }
}
